/*
 * Feed to DOM - 0.0.1
 * 
 * Copyright (c) 2008, Eric Ferraiuolo <eferraiuolo@gmail.com>. All rights reserved.
 * MIT License <http://www.opensource.org/licenses/mit-license.php>
 */

YUI.add('feed', function(Y){
	
	/**
	 * Create DOM elements for a JSON result from a Google Ajax Feed request.
	 * 
	 * @module feed
	 */
	
	var isString = Y.Lang.isString,
		isArray = Y.Lang.isArray,
		isValue = Y.Lang.isValue,
		isDate = Y.Lang.isDate,
		
		getClassName = Y.ClassNameManager.getClassName,
		
		BOUNDING_BOX = 'boundingBox',
		CONTENT_BOX = 'contentBox',
		
		FEED = 'feed',
		ENTRY = 'entry',
		HEADING = 'heading',
		ENTRIES = 'entries',
		CONTENT_FULL = 'content_full',
		CONTENT_SNIPPET = 'content_snippet',
		META = 'meta',
		AUTHOR = 'author',
		DESCRIPTION = 'description',
		PUBLISHED_DATE = 'published_date',
		CATEGORIES = 'categories',
		CATEGORY = 'category',
		
		DOT = '.',
		
		FEED_HEADING_CLASS = getClassName( FEED, HEADING ),
		FEED_META_CLASS = getClassName( FEED, META ),
		FEED_AUTHOR_CLASS = getClassName( FEED, AUTHOR ),
		FEED_DESCRIPTION_CLASS = getClassName( FEED, DESCRIPTION );
		FEED_ENTRIES_CLASS = getClassName( FEED, ENTRIES ),
		
		ENTRY_HEADING_CLASS = getClassName( ENTRY, HEADING ),
		ENTRY_META_CLASS = getClassName( ENTRY, META ),
		ENTRY_PUBLISHED_DATE_CLASS = getClassName( ENTRY, PUBLISHED_DATE ),
		ENTRY_CATEGORIES_CLASS = getClassName( ENTRY, CATEGORIES ),
		ENTRY_CATEGORY_CLASS = getClassName( ENTRY, CATEGORY ),
		ENTRY_CONTENT_FULL_CLASS = getClassName( ENTRY, CONTENT_FULL ),
		ENTRY_CONTENT_SNIPPET_CLASS = getClassName( ENTRY, CONTENT_SNIPPET );
	
	/**
	 * Create an XHTML DOM structure representing an RSS/Atom feed.
	 * 
	 * @class Feed
	 * @extends Widget
	 * @param config {Object} Configuration object
	 * @constructor
	 */
	function Feed() {
		Feed.superclass.constructor.apply( this, arguments );
	}
	
	/**
	 * The identity of the widget.
	 * 
	 * @property Feed.NAME
	 * @type String
	 * @static
	 */
	Feed.NAME = FEED;
	
	/**
	 * Static Object hash used to capture existing markup for progressive enhancement.
	 * Keys correspond to config attribute names and values are selectors used to inspect the contentBox for an existing node structure.
	 * 
	 * @property Feed.HTML_PARSER
	 * @type Object
	 * @static
	 */
	Feed.HTML_PARSER = {};
	
	/**
	 * Static property used to define the markup templates for the Node components of the Feed.
	 * 
	 * @property Feed.TEMPLATES
	 * @type Object
	 * @static
	 */
	Feed.TEMPLATES = {
		heading : '<h3><a></a></h3>',
		meta 	: '<div></div>',
		author	: '<p></p>',
		description : '<p></p>',
		entries : '<ul></ul>',
		entry	: '<li></li>'
	};
	
	/**
	 * Static property used to define the default attribute configuration of the Widget.
	 * 
	 * @property Feed.ATTRS
	 * @type Object
	 * @static
	 */
	Feed.ATTRS = {
		
		/**
		 * The feed title. Corresponds to the <title> element in Atom and the <title> element in RSS.
		 * 
		 * @attribute title
		 * @type String
		 * @default ''
		 */
		title : {
			value : '',
			validator : function (v) {
				return this._validateNewTitle( v );
			}
		},
		
		/**
		 * The URL for the HTML version of the feed. Corresponds to the <link> element in Atom and the <link> element in RSS.
		 * 
		 * @attribute link
		 * @type String
		 * @default ''
		 */
		link : {
			value : '',
			validator : function (v) {
				return this._validateNewLink( v );
			}
		},
		
		/**
		 * The feed description. Corresponds to the <subtitle> element in Atom and the <description> element in RSS.
		 * 
		 * @attribute description
		 * @type String
		 * @default ''
		 */
		description : {
			value : '',
			validator : function (v) {
				return this._validateNewDescription( v );
			}
		},
		
		/**
		 * The feed author. Corresponds to the <name> element for the author in Atom.
		 * 
		 * @attribute author
		 * @type String
		 * @default ''
		 */
		author : {
			value : '',
			validator : function (v) {
				return this._validateNewAuthor( v );
			}
		},
		
		/**
		 * A list of all of the entries in the feed. Corresponds to the <entry> element in Atom and the <item> element in RSS.
		 * 
		 * @attribute entries
		 * @type Array
		 * @default null
		 */
		entries : {
			value : null,
			validator : function (v) {
				return this._validateNewEntries( v );
			}
		}
		
	};
		
	Y.extend(Feed, Y.Widget, {
		
		/**
		 * Node to model a hyperlink as a heading using the Feed's link and title attributes.
		 * 
		 * @property _headingNode
		 * @type Node
		 * @protected
		 */
		_headingNode : null,
		
		/**
		 * Node to display the Feed's meta attribute.
		 * 
		 * @property _metaNode
		 * @type Node
		 * @protected
		 */
		_metaNode : null,
		
		/**
		 * Node to display the list of entries for the Feed.
		 * 
		 * @property _entriesNode
		 * @type Node
		 * @protected
		 */
		_entriesNode : null,
		
		/**
		 * Collection of Entry Widget instances corresponding to the list in the entries attribute.
		 * 
		 * @property _entries
		 * @type Array
		 * @protected
		 */
		_entries : [],
		
		/**
		 * Cleans up node references for the heading, meta, and entries nodes created during render.
		 * 
		 * @method destructor
		 * @protected
		 */
		destructor : function() {
			this._headingNode = null;
			this._metaNode = null;
			this._entriesNode = null;
			this._distroyEntries();
		},
		
		/**
		 * Create a DOM structure for the Feed.
		 * 
		 * @method rednerUI
		 * @protected
		 */
		renderUI : function() {
			var headingNode = Y.Node.create( Feed.TEMPLATES.heading ).addClass( FEED_HEADING_CLASS ),
				metaNode = Y.Node.create( Feed.TEMPLATES.meta ).addClass( FEED_META_CLASS ),
				authorNode = Y.Node.create( Feed.TEMPLATES.author ).addClass( FEED_AUTHOR_CLASS ),
				descriptionNode = Y.Node.create( Feed.TEMPLATES.description ).addClass( FEED_DESCRIPTION_CLASS ),
				entriesNode = Y.Node.create( Feed.TEMPLATES.entries ).addClass( FEED_ENTRIES_CLASS );
			
			metaNode.appendChild( descriptionNode );
			metaNode.appendChild( authorNode );
			
			this._headingNode = this.get(CONTENT_BOX).appendChild( headingNode );
			this._metaNode = this.get(CONTENT_BOX).appendChild( metaNode );
			this._entriesNode = this.get(CONTENT_BOX).appendChild( entriesNode );
		},
		
		/**
		 * Listens for certain attribute changes and calls the corresponding handler.
		 * 
		 * @method bindUI
		 * @protected
		 */
		bindUI : function() {
			this.after('titleChange', this._afterTitleChange);
			this.after('linkChange', this._afterLinkChange);
			this.after('descriptionChange', this._afterDescriptionChange);
			this.after('authorChange', this._afterAuthorChange);
			this.after('entriesChange', this._afterEntriesChange);
		},
		
		/**
		 * Synchronizes the DOM state with the attribute settings.
		 * 
		 * @method syncUI
		 */
		syncUI : function() {
			this._uiSetTitle( this.get('title') );
			this._uiSetLink( this.get('link') );
			this._uiSetDescription( this.get('description') );
			this._uiSetAuthor( this.get('author') );
			if ( this._validateNewEntries( this.get('entries') ) )
				this._uiSetEntries( this.get('entries') );
		},
		
		/**
		 * Distroys all Entry instnaces and Nodes from the entriesNode element.
		 * 
		 * @method _distroyEntries
		 * @protected
		 */
		_distroyEntries : function() {
			for ( var i = 0; i < this._entries.length; i++ ) {
				this._entriesNode.removeChild( this._entries[i].get(BOUNDING_BOX) );
				delete this._entries[i];
			}
			this._entries = [];
		},
		
		/**
		 * Validator applied to the title attribute. Only Strings allowed.
		 * 
		 * @method _validateNewTitle
		 * @param v {String} proposed value for the title attribute
		 * @return Boolean
		 * @protected
		 */
		_validateNewTitle : function (v) {
			return isString( v );
		},
		
		/**
		 * Validator applied to the link attribute. Only Strings allowed.
		 * 
		 * @method _validateNewLink
		 * @param v {String} proposed value for the link attribute
		 * @return Boolean
		 * @protected
		 */
		_validateNewLink : function (v) {
			return isString( v );
		},
		
		/**
		 * Validator applied to the description attribute. Only Strings allowed.
		 * 
		 * @method _validateNewDescription
		 * @param v {String} proposed value for the description attribute
		 * @return Boolean
		 * @protected
		 */
		_validateNewDescription : function (v) {
			return isString( v );
		},
		
		/**
		 * Validator applied to the author attribute. Only Strings allowed.
		 * 
		 * @method _validateNewAuthor
		 * @param v {String} proposed value for the author attribute
		 * @return Boolean
		 * @protected
		 */
		_validateNewAuthor : function (v) {
			return isString( v );
		},
		
		/**
		 * Validator applied to the entries attribute. Only Arrays allowed.
		 * 
		 * @method _validateNewEntries
		 * @param v {Array} proposed value for the entries attribute
		 * @return Boolean
		 * @protected
		 */
		_validateNewEntries : function (v) {
			return isArray( v );
		},
		
		/**
		 * Replaces the hyperlinked text in the headng element in response to the change in the Feed's title attribute.
		 * 
		 * @method _afterTitleChange
		 * @protected
		 */
		_afterTitleChange : function (e) {
			this._uiSetTitle(e.newVal);
		},
		
		/**
		 * Replaces the URL of the hyperlinked text in the heading element in respose to the change in the Feed's link attribute.
		 * 
		 * @method _afterLinkChange
		 * @protected
		 */
		_afterLinkChange : function (e) {
			this._uiSetLink(e.newVal);
		},
		
		/**
		 * Replaces the description text in the meta element in respose to the change in the Feed's description attribute.
		 * 
		 * @method _afterDescriptionChange
		 * @protected
		 */
		_afterDescriptionChange : function (e) {
			this._uiSetDescription(e.newVal);
		},
		
		/**
		 * Replaces the author text in the meta element in respose to the change in the Feed's author attribute.
		 * 
		 * @method _afterAuthorChange
		 * @protected
		 */
		_afterAuthorChange : function (e) {
			this._uiSetAuthor(e.newVal);
		},
		
		/**
		 * Replaces the list of entries in the entries node in respose to the change in the Feed's entries attribute.
		 * 
		 * @method _afterEntriesChange
		 * @protected
		 */
		_afterEntriesChange : function (e) {
			this._uiSetEntries(e.newVal);
		},
		
		/**
		 * Displays the Feed's title as hyperlinked text in the heading element.
		 * 
		 * @method _uiSetTitle
		 * @protected
		 */
		_uiSetTitle : function (v) {
			this._headingNode.query('a').set('innerHTML', v);
		},
		
		/**
		 * Makes the Feed's hyperlinked title in the heading element link to the URL in the Feed's link attribute.
		 * 
		 * @method _uiSetLink
		 * @protected
		 */
		_uiSetLink : function (v) {
			this._headingNode.query('a').set('href', v);
		},
		
		/**
		 * Displays the Feed's description text in the meta node.
		 * 
		 * @method _uiSetDescription
		 * @protected
		 */
		_uiSetDescription : function (v) {
			this._metaNode.query(DOT+FEED_DESCRIPTION_CLASS).set('innerHTML', v);
		},
		
		/**
		 * Displays the Feed's author text in the meta node.
		 * 
		 * @method _uiSetAuthor
		 * @protected
		 */
		_uiSetAuthor : function (v) {
			this._metaNode.query(DOT+FEED_AUTHOR_CLASS).set('innerHTML', v);
		},
		
		/**
		 * Displays the Feed's entries as a list in the entries node.
		 * 
		 * @method _uiSetEntries
		 * @protected
		 */
		_uiSetEntries : function (v) {
			var entry = null;
			this._distroyEntries();
			for ( var i = 0; i < v.length; i++ ) {
				entry = new Entry( Y.merge(v[i], { boundingBox:Y.Node.create( Feed.TEMPLATES.entry ) }) );
				entry.render( this._entriesNode );
				this._entries.push( entry );
			}
		}
		
	});
	
	
	/**
	 * Create an XHTML DOM structure representing an RSS/Atom feed entry.
	 * 
	 * @class Entry
	 * @extends Widget
	 * @param config {Object} Configuration object
	 * @constructor
	 */
	function Entry() {
		Entry.superclass.constructor.apply( this, arguments );
	}
	
	/**
	 * The identity of the widget.
	 * 
	 * @property Entry.NAME
	 * @type String
	 * @static
	 */
	Entry.NAME = ENTRY;
	
	/**
	 * Static Object hash used to capture existing markup for progressive enhancement.
	 * Keys correspond to config attribute names and values are selectors used to inspect the contentBox for an existing node structure.
	 * 
	 * @property Entry.HTML_PARSER
	 * @type Object
	 * @static
	 */
	Entry.HTML_PARSER = {};
	
	/**
	 * Static property used to define the markup templates for the components of the Entry.
	 * 
	 * @property Entry.TEMPLATES
	 * @type String
	 * @static
	 */
	Entry.TEMPLATES = {
		heading : '<h4><a></a></h4>',
		meta : '<div></div>',
		publishedDate : '<p><abbr></abbr></p>',
		categories : '<ul></ul>',
		category : '<li></li>',
		contentSnippet : '<p></p>',
		contentFull : '<div></div>'
	};
	
	/**
	 * Static property used to define the default attribute configuration of the Widget.
	 * 
	 * @property Entry.ATTRS
	 * @type Object
	 * @static
	 */
	Entry.ATTRS = {
		
		/**
		 * The entry title. Corresponds to the <title> element in Atom and the <title> element in RSS.
		 * 
		 * @attribute title
		 * @type String
		 * @default ''
		 */
		title : {
			value : '',
			validator : function (v) {
				return this._validateNewTitle( v );
			}
		},
		
		/**
		 * The URL for the HTML version of the entry. Corresponds to the <link> element in Atom and the <link> element in RSS.
		 * 
		 * @attribute link
		 * @type String
		 * @default ''
		 */
		link : {
			value : '',
			validator : function (v) {
				return this._validateNewLink( v );
			}
		},
		
		/**
		 * The body of this entry, inlcuding HTML tags. Since this value can contain HTML tags, 
		 * you should display this value using elem.innerHTML = entry.content (as opposed to using document.createTextNode). 
		 * Corresponds to the <content> or <summary> elements in Atom and the <description> element in RSS.
		 * 
		 * @attribute content
		 * @type String
		 * @default ''
		 */
		content : {
			value : '',
			validator : function (v) {
				return this._validateNewContent( v );
			}
		},
		
		/**
		 * A snippet (< 120 characters) version of the content attribute. The snippet does not contain any HTML tags.
		 * 
		 * @attribute contentSnippet
		 * @type String
		 * @default ''
		 */
		contentSnippet : {
			value : '',
			validator : function (v) {
				return this._validateNewContentSnippet( v );
			}
		},
		
		/**
		 * The string date on which the entry was published of the form "13 Apr 2007 12:40:07 -0700". 
		 * You can parse the date with new Date(entry.date). 
		 * Corresponds to the <published> element in Atom and the <pubDate> element in RSS.
		 * 
		 * @attribute publishedDate
		 * @type Date
		 * @default null
		 */
		publishedDate : {
			value : null,
			validator : function (v) {
				return this._validateNewPublishedDate( v );
			}
		},
		
		/**
		 * A list of string tags for the entry. 
		 * Corresponds to the term attribute for the <category> element in Atom and the <category> element in RSS.
		 * 
		 * @attribute categories
		 * @type Array
		 * @default null
		 */
		categories : {
			value : null,
			validator : function (v) {
				return this._validateNewCategories( v );
			}
		}
		
	};
		
	Y.extend(Entry, Y.Widget, {
		
		/**
		 * Node to model a hyperlink as a heading using the Entry's link and title attributes.
		 * 
		 * @property _headingNode
		 * @type Node
		 * @protected
		 */
		_headingNode : null,
		
		/**
		 * Node to display the Entry's meta data.
		 * 
		 * @property _metaNode
		 * @type Node
		 * @protected
		 */
		_metaNode : null,
		
		/**
		 * Node to display the Entry's content.
		 * 
		 * @property _contentFullNode
		 * @type Node
		 * @protected
		 */
		_contentFullNode : null,
		
		/**
		 * Node to display the Entry's content snippet, this Node's parent is the content node.
		 * 
		 * @property _contentSnippet
		 * @type Node
		 * @protected
		 */
		_contentSnippetNode : null,
		
		/**
		 * Cleans up node references for the heading, meta, and content nodes created during render.
		 * 
		 * @method destructor
		 * @protected
		 */
		destructor : function() {
			this._headingNode = null;
			this._metaNode = null;
			this._contentFullNode = null;
			this._contentSnippet = null;
		},
		
		/**
		 * Create a DOM structure for the Entry.
		 * 
		 * @method rednerUI
		 * @protected
		 */
		renderUI : function() {
			var headingNode = Y.Node.create( Entry.TEMPLATES.heading ).addClass( ENTRY_HEADING_CLASS ),
				metaNode = Y.Node.create( Entry.TEMPLATES.meta ).addClass( ENTRY_META_CLASS ),
				publishedDateNode = Y.Node.create( Entry.TEMPLATES.publishedDate ).addClass( ENTRY_PUBLISHED_DATE_CLASS ),
				categoriesNode = Y.Node.create( Entry.TEMPLATES.categories ).addClass( ENTRY_CATEGORIES_CLASS ),
				contentFullNode = Y.Node.create( Entry.TEMPLATES.contentFull ).addClass( ENTRY_CONTENT_FULL_CLASS ),
				contentSnippetNode = Y.Node.create( Entry.TEMPLATES.contentSnippet ).addClass( ENTRY_CONTENT_SNIPPET_CLASS );
			
			metaNode.appendChild( publishedDateNode );
			metaNode.appendChild( categoriesNode );
			
			this._headingNode = this.get(CONTENT_BOX).appendChild( headingNode );
			this._metaNode = this.get(CONTENT_BOX).appendChild( metaNode );
			this._contentSnippetNode = this.get(CONTENT_BOX).appendChild( contentSnippetNode );
			this._contentFullNode = this.get(CONTENT_BOX).appendChild( contentFullNode );
		},
		
		/**
		 * Listens for certain attribute changes and calls the corresponding handler.
		 * 
		 * @method bindUI
		 * @protected
		 */
		bindUI : function() {
			this.after('titleChange', this._afterTitleChange);
			this.after('linkChange', this._afterLinkChange);
			this.after('contentChange', this._afterContentChange);
			this.after('contentSnippetChange', this._afterContentSnippetChange);
			this.after('publishedDateChange', this._afterPublishedDateChange);
			this.after('categoriesChange', this._afterCategoriesChange);
		},
		
		/**
		 * Synchronizes the DOM state with the attribute settings.
		 * 
		 * @method syncUI
		 */
		syncUI : function() {
			this._uiSetTitle( this.get('title') );
			this._uiSetLink( this.get('link') );
			this._uiSetContent( this.get('content') );
			this._uiSetContentSnippet( this.get('contentSnippet') );
			
			if ( this._validateNewPublishedDate( this.get('publishedDate') ) )
				this._uiSetPublishedDate( this.get('publishedDate') );
				
			if ( this._validateNewCategories( this.get('categories') ) )
				this._uiSetCategories( this.get('categories') );
		},
		
		/**
		 * Validator applied to the title attribute. Only Strings allowed.
		 * 
		 * @method _validateNewTitle
		 * @param v {String} proposed value for the title attribute
		 * @return Boolean
		 * @protected
		 */
		_validateNewTitle : function (v) {
			return isString( v );
		},
		
		/**
		 * Validator applied to the link attribute. Only Strings allowed.
		 * 
		 * @method _validateNewLink
		 * @param v {String} proposed value for the link attribute
		 * @return Boolean
		 * @protected
		 */
		_validateNewLink : function (v) {
			return isString( v );
		},
		
		/**
		 * Validator applied to the content attribute. Only Strings allowed.
		 * 
		 * @method _validateNewContent
		 * @param v {String} proposed value for the content attribute
		 * @return Boolean
		 * @protected
		 */
		_validateNewContent : function (v) {
			return isString( v );
		},
		
		/**
		 * Validator applied to the contentSnippet attribute. Only Strings allowed.
		 * 
		 * @method _validateNewContentSnippet
		 * @param v {String} proposed value for the contentSnippet attribute
		 * @return Boolean
		 * @protected
		 */
		_validateNewContentSnippet : function (v) {
			return isString( v );
		},
		
		/**
		 * Validator applied to the publishedDate attribute. Only Dates allowed.
		 * 
		 * @method _validateNewPublishedDate
		 * @param v {Date} proposed value for the publishedDate attribute
		 * @return Boolean
		 * @protected
		 */
		_validateNewPublishedDate : function (v) {
			return isValue(v) && isDate( new Date(v) );
		},
		
		/**
		 * Validator applied to the categories attribute. Only Arrays allowed.
		 * 
		 * @method _validateNewCategories
		 * @param v {Array} proposed value for the categories attribute
		 * @return Boolean
		 * @protected
		 */
		_validateNewCategories : function (v) {
			return isArray( v );
		},
		
		/**
		 * Replaces the hyperlinked text in the headng node in response to the change in the Enty's title attribute.
		 * 
		 * @method _afterTitleChange
		 * @protected
		 */
		_afterTitleChange : function (e) {
			this._uiSetTitle(e.newVal);
		},
		
		/**
		 * Replaces the URL of the hyperlinked text in the heading node in respose to the change in the Entry's link attribute.
		 * 
		 * @method _afterLinkChange
		 * @protected
		 */
		_afterLinkChange : function (e) {
			this._uiSetLink(e.newVal);
		},
		
		/**
		 * Replaces the content text in the content node in respose to the change in the Entry's content attribute.
		 * 
		 * @method _afterContentChange
		 * @protected
		 */
		_afterContentChange : function (e) {
			this._uiSetContent(e.newVal);
		},
		
		/**
		 * Replaces the contentSnippet text in the content node in respose to the change in the Entry's contentSnippet attribute.
		 * 
		 * @method _afterContentSnippetChange
		 * @protected
		 */
		_afterContentSnippetChange : function (e) {
			this._uiSetContentSnippet(e.newVal);
		},
		
		/**
		 * Replaces the publsihedDate text in the meta node in respose to the change in the Entry's publishedDate attribute.
		 * 
		 * @method _afterPublishedDateChange
		 * @protected
		 */
		_afterPublishedDateChange : function (e) {
			this._uiSetPublishedDate(e.newVal);
		},
		
		/**
		 * Replaces the categories list in the meta node in respose to the change in the Entry's categories attribute.
		 * 
		 * @method _afterCategoriesChange
		 * @protected
		 */
		_afterCategoriesChange : function (e) {
			this._uiSetCategories(e.newVal);
		},
		
		/**
		 * Displays the Entry's title as hyperlinked text in the heading node.
		 * 
		 * @method _uiSetTitle
		 * @protected
		 */
		_uiSetTitle : function (v) {
			this._headingNode.query('a').set('innerHTML', v);
		},
		
		/**
		 * Makes the Entry's hyperlinked title in the heading node link to the URL in the Entry's link attribute.
		 * 
		 * @method _uiSetLink
		 * @protected
		 */
		_uiSetLink : function (v) {
			this._headingNode.query('a').set('href', v);
		},
		
		/**
		 * Displays the Entry's content text in the content node.
		 * 
		 * @method _uiSetContent
		 * @protected
		 */
		_uiSetContent : function (v) {
			this._contentFullNode.set('innerHTML', v);
		},
		
		/**
		 * Displays the Entry's contentSnippet in the content node.
		 * 
		 * @method _uiSetContentSnippet
		 * @protected
		 */
		_uiSetContentSnippet : function (v) {
			this._contentSnippetNode.set('innerHTML', v);
		},
		
		/**
		 * Displays the Entry's publishedDate in the meta node.
		 * 
		 * @method _uiSetPublishedDate
		 * @protected
		 */
		_uiSetPublishedDate : function (v) {
			this._metaNode.query(DOT+ENTRY_PUBLISHED_DATE_CLASS + ' abbr').set( 'title', v ).set( 'innerHTML', new Date(v).toDateString() );
		},
		
		/**
		 * Displays the Entry's categories in the meta node.
		 * 
		 * @method _uiSetCategories
		 * @protected
		 */
		_uiSetCategories : function (v) {
			var categoriesNode = this._metaNode.query(DOT+ENTRY_CATEGORIES_CLASS),
				category = null;
			
			// Remove old categories (child nodes)
			while ( categoriesNode.hasChildNodes() ) {
				categoriesNode.removeChild( categoriesNode.get('firstChild') );
			}
			
			// Add new categoires as child nodes to the categories node
			for ( var i = 0; i < v.length; i++ ) {
				category = Y.Node.create( Entry.TEMPLATES.category );
				category.addClass( ENTRY_CATEGORY_CLASS ).set( 'innerHTML', v[i] );
				categoriesNode.appendChild( category );
			}
		}
		
	});
	
	Y.Feed = Feed;
	Y.Entry = Entry;
	
}, '0.0.1', { requires:['widget'] });
